package com.xxx.mybaitis;

import java.lang.reflect.Field;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

/**
 * 具体的执行器：操作数据库的操作
 */
public class DefaultExecutor implements Executor {
	
	private Configuration configuration;
	
	public DefaultExecutor(Configuration configuration) {
		super();
		this.configuration = configuration;
	}

	/**
	 * 查询数据库
	 */
	@Override
	public <E> List<E> query(MappedStatement statement, Object parameter) {
		System.out.println("sql:"+statement.getSql());
		System.out.println("parameter:"+parameter);
		List<E> list = new ArrayList<>();
		
		Connection connection = null;
		PreparedStatement preparedStatement = null; // sql语句
        ResultSet resultSet = null; // 返回结果
        
        try {
        	// 1.获取一个connection
    		connection = Connections.getConnection(configuration);
    		
        	// 2.创建预编译PreparedStatement对象，从MapperStatement对象中获取sql
			preparedStatement = connection.prepareStatement(statement.getSql());
			
			// 3.处理占位符：？
			handleParameter(preparedStatement, parameter);
			
			// 4.执行查询操作，获取resultSet
			resultSet = preparedStatement.executeQuery();
			
			// 5.将结果集进行反射，然后填充到list里
			handleResult(resultSet, list, statement.getResultType());
		} catch (SQLException e) {
			e.printStackTrace();
		} finally {
			try {
				if (resultSet != null) {
	                resultSet.close();
	            }
	            if (preparedStatement != null) {
	                preparedStatement.close();
	            }
	            connection.close();
	        } catch (SQLException e) {
	            e.printStackTrace();
	        }
		}
		
		return list;
	}

	/**
	 * 更新数据库（包括insert、update、delete）
	 */
	@Override
	public int update(MappedStatement statement, Object parameter) throws SQLException {
		System.out.println("sql:"+statement.getSql());
		System.out.println("parameter:"+parameter);
		Connection connection = null;
		PreparedStatement preparedStatement = null; // sql语句
		int result = 0;
        
        try {
        	// 1.获取一个connection
    		connection = Connections.getConnection(configuration);
    		
        	// 2.创建预编译PreparedStatement对象，从MapperStatement对象中获取sql
			preparedStatement = connection.prepareStatement(statement.getSql());
			
			// 3.处理占位符：？
			handleParameter(preparedStatement, parameter);
			
			// 4.执行更新操作，获取resultSet
			result = preparedStatement.executeUpdate();
		} catch (SQLException e) {
			e.printStackTrace();
		} finally {
			try {
	            if (preparedStatement != null) {
	                preparedStatement.close();
	            }
	            connection.close();
	        } catch (SQLException e) {
	            e.printStackTrace();
	        }
		}
        
		return result;
	}

	/**
	 * 处理返回结果
	 * @param resultSet
	 * @param list
	 * @param resultType
	 */
	private <E> void handleResult(ResultSet rs, List<E> list, String className) {
		// 通过反射获取类的对象
		Class<E> clazz = null;
		try {
			clazz = (Class<E>) Class.forName(className);
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
		
		try {
			while(rs.next()) {
				// 通过反射实例化对象
				Object object = clazz.newInstance();
				// 使用反射工具将ResultSet中的数据填充到className对象里
				Field[] fields = clazz.getDeclaredFields();
				for(Field field : fields) {
					// 获取集合中数据
					Object value = rs.getObject(field.getName());
					// 设置允许私有访问
					field.setAccessible(true);
					// 参数赋值
					field.set(object, value);
				}
				list.add((E)object);
			}
		} catch (SQLException | InstantiationException | IllegalAccessException e) {
			e.printStackTrace();
		}
	}

	/**
	 * 处理占位符
	 * @param preparedStatement
	 * @param parameter
	 * @throws SQLException 
	 */
	private void handleParameter(PreparedStatement preparedStatement, Object parameter) throws SQLException {
		if(parameter==null) {
			return;
		}
		Object[] args = (Object[]) parameter;
		int len = args.length;
		for(int i=0; i<len; i++) {
			if (args[i] instanceof Integer) {
				preparedStatement.setInt(i+1, (Integer) args[i]);
	        } else if (args[i] instanceof Long) {
	        	preparedStatement.setLong(i+1, (Long) args[i]);
	        } else if (args[i] instanceof String) {
	        	preparedStatement.setString(i+1, (String) args[i]);
	        }
		}
	}
	
}
